package com.sxt.permissionhelperlib;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PerMissionHelper {

    private static final String PROXY_SUFFIX = "$$PermissionProxy";

    private static final Set<String> mPermissions;

    private static final String TAG = PerMissionHelper.class.getSimpleName();

    static {
        mPermissions = initializePermissionsMap();
    }

    @SuppressWarnings("unused")
    public static void requestPermission(Activity activity, int requestCode, String... permissions) {
        doRequestPermission(activity, permissions, requestCode);
    }

    @SuppressWarnings("unused")
    public static void requestPermission(Fragment fragment, int requestCode, String... permissions) {
        doRequestPermission(fragment.getActivity(), permissions, requestCode);
    }

    private static Set<String> initializePermissionsMap() {
        Set<String> permissions = new HashSet<String>(1);
        Field[] fields = Manifest.permission.class.getFields();
        for (Field field : fields) {
            String name = null;
            try {
                name = (String) field.get("");
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Could not access field", e);
            }
            permissions.add(name);
        }
        return permissions;
    }

    private static void doRequestPermission(Activity activity, String[] permissions, int requestCode) {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
            doExecuteGrant(activity, requestCode, permissions);
            return;
        }
        // 是否有未授权的权限
        List<String> deniedPermissions = findDeniedPermissions(activity, permissions);
        if (deniedPermissions.isEmpty()) {
            // 已经获取了所有的权限，不需要再授权
            doExecuteGrant(activity, requestCode, permissions);
            return;
        }
        // 还有权限未被授权，需要获取
        List<String> needUserAllowPermission = shouldAllowPermissionByUser(activity, deniedPermissions);
        List<String> needRationalePermission = shouldShowUserPermissionRationale(activity, deniedPermissions);
        if (!needUserAllowPermission.isEmpty()) {
            doExecuteDenied(activity, requestCode, permissions);
        }
        if (!needRationalePermission.isEmpty()) {
            boolean rationale = doExecuteRationale(activity, requestCode, needRationalePermission.toArray(new String[0]));
//            if (rationale) {
//                _doRequestPermission(activity, needRationalePermission.toArray(new String[0]), requestCode);
//            }
        }
//        boolean Rationale = shouldShowPermissionRationale(activity, requestCode, permissions);
//        if (Rationale) {
//            return;
//        }
    }

    private static List<String> shouldShowUserPermissionRationale(Activity activity, List<String> deniedPermissions) {
        ArrayList<String> needRationalePermission = new ArrayList<>();
        for (String permission : deniedPermissions) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                needRationalePermission.add(permission);
            }
        }
        return needRationalePermission;
    }

    private static List<String> shouldAllowPermissionByUser(Activity activity, List<String> deniedPermissions) {
        ArrayList<String> needUserSelfAllowPermission = new ArrayList<>();
        for (String permission : deniedPermissions) {
            if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                needUserSelfAllowPermission.add(permission);
            }
        }
        return needUserSelfAllowPermission;
    }

    private static synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {
        return context != null && (ActivityCompat.checkSelfPermission(context, permission)
                == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission));
    }

    private static void _doRequestPermission(Activity activity, String[] permissions, int requestCode) {
//        List<String> deniedPermissions = findReallyDeniedPermissions(activity, permissions);
        List<String> deniedPermissions = Arrays.asList(permissions);
        if (deniedPermissions.size() > 0) {
            ActivityCompat.requestPermissions(activity, deniedPermissions.toArray(new String[0]), requestCode);
        } else {
            doExecuteGrant(activity, requestCode, permissions);
        }
    }

    private static boolean shouldShowPermissionRationale(Activity activity, int requestCode, String[] permissions) {
        PermissionProxy permissionProxy = findProxy(activity);
        List<String> deniedPermissions = findDeniedPermissionsButRationale(activity, permissions);
        if (!deniedPermissions.isEmpty()) {
            return permissionProxy.rational(requestCode,
                    activity,
                    deniedPermissions.toArray(new String[deniedPermissions.size()]),
                    () -> {
                        doRequestPermission(activity, permissions, requestCode);
                    });
        }
        return false;
    }

    private static List<String> findDeniedPermissionsButRationale(Activity activity, String[] permissions) {
        ArrayList<String> denied = new ArrayList<>();
        for (String permission : permissions) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                denied.add(permission);
            }
        }
        return denied;
    }

    private static List<String> findDeniedPermissions(Activity activity, String[] permissions) {
        ArrayList<String> denied = new ArrayList<>();
        for (String permission : permissions) {
            if (!hasPermission(activity, permission)) {
                denied.add(permission);
            }
        }
        return denied;
    }

    private static List<String> findReallyDeniedPermissions(Activity activity, String[] permissions) {
        ArrayList<String> denied = new ArrayList<>();
        for (String permission : permissions) {
            if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                denied.add(permission);
            }
        }
        return denied;
    }

    private static void doExecuteGrant(Activity activity, int requestCode, String[] permissions) {
        PermissionProxy permissionProxy = findProxy(activity);
        if (permissionProxy != null) {
            permissionProxy.grant(requestCode, activity, permissions);
        }
    }

    private static void doExecuteDenied(Activity activity, int requestCode, String[] permissions) {
        PermissionProxy permissionProxy = findProxy(activity);
        if (permissionProxy != null) {
            permissionProxy.denied(requestCode, activity, permissions);
        }
    }

    private static boolean doExecuteRationale(Activity activity, int requestCode, String[] permissions) {
        PermissionProxy permissionProxy = findProxy(activity);
        List<String> deniedPermissions = Arrays.asList(permissions);
        if (!deniedPermissions.isEmpty()) {
            return permissionProxy.rational(requestCode,
                    activity,
                    deniedPermissions.toArray(new String[deniedPermissions.size()]),
                    () -> {
                        _doRequestPermission(activity, permissions, requestCode);
                    });
        }
        return false;
    }

    private static PermissionProxy findProxy(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        try {
            Class<?> proxyClass = Class.forName(aClass.getName() + PROXY_SUFFIX);
            try {
                return (PermissionProxy) proxyClass.newInstance();
            } catch (IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void onRequestPermissionResult(Activity activity, int requestCode, String[] permissions, int[] grantResults) {
        List<String> grant = new ArrayList<>();
        List<String> denied = new ArrayList<>();
        for (int i = 0; i < grantResults.length; i++) {
            String permission = permissions[i];
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                denied.add(permission);
            } else {
                grant.add(permission);
            }
        }
        if (grant.size() > 0) {
            doExecuteGrant(activity, requestCode, permissions);
        }
        if (denied.size() > 0) {
            doExecuteDenied(activity, requestCode, permissions);
        }
    }

    @SuppressWarnings("ConstantConditions")
    @SuppressLint("ObsoleteSdkInt")
    public static void simpleSetting(Context context) {
        Intent intent = new Intent();
        try{
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            // 将用户引导到系统设置页面
            if (Build.VERSION.SDK_INT >= 9) {
                Log.e("HLQ_Struggle", "APPLICATION_DETAILS_SETTINGS");
                intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
                intent.setData(Uri.fromParts("package", context.getPackageName(), null));
            } else if (Build.VERSION.SDK_INT <= 8) {
                intent.setAction(Intent.ACTION_VIEW);
                intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
                intent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
            }
            context.startActivity(intent);
        } catch (Exception e) {//抛出异常就直接打开设置页面
            Log.e("HLQ_Struggle", e.getLocalizedMessage());
            intent = new Intent(Settings.ACTION_SETTINGS);
            context.startActivity(intent);
        }
    }
}
